Skip to content

Extract Date/Time intervals from Periods via cqf-cqlType and render interval/quantity actuals in CQL syntax - #109

Open
alexzautke wants to merge 3 commits into
mainfrom
claude/time-interval-extraction
Open

Extract Date/Time intervals from Periods via cqf-cqlType and render interval/quantity actuals in CQL syntax#109
alexzautke wants to merge 3 commits into
mainfrom
claude/time-interval-extraction

Conversation

@alexzautke

@alexzautke alexzautke commented Jul 31, 2026

Copy link
Copy Markdown

Problem

Temporal intervals whose point type is not DateTime always failed comparison. FHIR Period boundaries are dateTimes, so engines serialize Interval<System.Time> and Interval<System.Date> as Periods and declare the real point type in the cqf-cqlType extension:

{
  "extension": [{ "url": ".../cqf-cqlType", "valueString": "Interval<System.Time>" }],
  "valuePeriod": { "start": "0001-01-01T00:00:00.000", "end": "0001-01-01T23:59:59.599" }
}

DateTimeIntervalExtractor ignored the extension (a long-standing TODO) and formatted every boundary as a DateTime literal:

  • Time intervals kept their placeholder date anchor: @0001-01-01T00:00:00.000 never matches the expected @T00:00:00.000 (e.g. CqlIntervalOperatorsTest / Interval / TimeIntervalTest).
  • Date intervals got a trailing T appended: @2018-01-01T never matches the expected @2018-01-01 (e.g. CqlIntervalOperatorsTest / Expand / ExpandPerDay).

Changes

  • DateTimeIntervalExtractor dispatches on cqf-cqlType: the declared point type (Date, DateTime or Time; System. prefix optional, DateTime when absent) picks the boundary formatter. Time boundaries drop the date anchor and any timezone offset (a CQL Time carries neither) and become @T… literals; Date boundaries keep the @date form (no trailing T) and drop any anchored time part. Periods without the extension behave exactly as before.
  • actual values render in CQL syntax in the results file: interval-shaped actuals now serialize as Interval[@T00:00:00.000, @T23:59:59.599] (brackets/parens per closed flag, recursing into boundaries) and {value, unit} quantities as 0.0002 'm2', instead of raw JSON — mirroring the expected value's CQL/CVL notation, following the precedent set for lists in Render list results in CQL list syntax in the results file #107. Other objects keep the JSON rendering.

Verification

  • End-to-end checks of both reported payloads: the TimeIntervalTest Period now extracts to {low: '@T00:00:00.000', high: '@T23:59:59.599', …} and the ExpandPerDay list of four date Periods extracts to Interval[@2018-01-01, @2018-01-01] … boundaries; resultsEqual against the cvl-parsed expected values returns true for both.
  • New unit tests for the extractor (Time and Date intervals, with/without the System. prefix, offset and time-part stripping, missing boundary, extension-less and DateTime-typed Periods unchanged, list of date intervals) and for the results-file rendering (intervals, quantities, quantity-boundary intervals, non-interval objects unchanged). Full suite: 139 passed, tsc --noEmit clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Qw9VWh3fvEPFMArBxnxtXF

claude added 2 commits July 31, 2026 09:00
FHIR Period boundaries are dateTimes, so engines serialize a time
interval by anchoring the times to a placeholder date (e.g. 0001-01-01)
and declaring the real point type in the cqf-cqlType extension. The
DateTimeIntervalExtractor ignored that extension and produced datetime
literals (@0001-01-01T00:00:00.000), which never match the expected
time literals (@t00:00:00.000), failing tests like TimeIntervalTest.

Honor the extension: when it declares Interval<System.Time> (System.
prefix optional), strip the date anchor and any timezone offset - a CQL
Time carries neither - and emit @t time literals.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qw9VWh3fvEPFMArBxnxtXF
Interval and quantity actual values were serialized as raw JSON
('{"lowClosed":true,...}', '{"value":0.0002,"unit":"m2"}'),
which reads poorly next to the expected value's CQL notation.

Render interval-shaped actuals as Interval[low, high) (brackets per
closed flag, recursing into the boundaries) and {value, unit} quantity
actuals as value 'unit'. Other objects keep the JSON rendering.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qw9VWh3fvEPFMArBxnxtXF

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes incorrect extraction/serialization of Interval<System.Time> values coming from FHIR Period parameters by honoring the cqf-cqlType extension, and improves test-results readability by rendering interval- and quantity-shaped actuals in CQL syntax (matching the expected CQL/CVL notation).

Changes:

  • Update DateTimeIntervalExtractor to detect cqf-cqlType of Interval<(System.)?Time> and format Period boundaries as CQL time literals (@T...), stripping placeholder dates and offsets.
  • Extend results-file actual-value formatting to render intervals as Interval[low, high) / Interval[low, high] and quantities as value 'unit', while keeping JSON rendering for other objects.
  • Add unit tests covering time-interval extraction variants and results-file rendering for intervals/quantities.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
test/extractResults-cql_operations.test.ts Adds extractor unit tests for cqf-cqlType time intervals (prefix/no-prefix, offset stripping, missing boundary, non-time unchanged).
test/cql-test-results-validator.test.ts Adds unit tests asserting results JSON renders interval/quantity actuals in CQL syntax and preserves JSON for other objects.
src/test-results/cql-test-results.ts Enhances formatActualValue to render intervals and quantities (in addition to lists) in CQL syntax for results output and schema validation.
src/extractors/value-type-extractors/value-type-extractor-utils.ts Introduces format_time helper to convert anchored dateTimes into CQL @T... literals (stripping offset).
src/extractors/value-type-extractors/datetime-interval-extractor.ts Implements cqf-cqlType detection for Interval<Time> and uses format_time vs format_datetime accordingly.
Suppressed comments (1)

src/extractors/value-type-extractors/datetime-interval-extractor.ts:40

  • highClosed is currently hard-coded to true even when the Period has no end and high becomes null. This produces an inconsistent/invalid interval shape (closed null boundary) and differs from the QuantityIntervalExtractor behavior, which derives highClosed from whether high is present.
				: null;
			return {
				lowClosed: low !== null,
				low: low,
				highClosed: true,

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Same mapping gap as time intervals: a date interval arrives as a
valuePeriod whose boundaries format_datetime turns into DateTime
literals (@2018-01-01T), which never match the expected Date literals
(@2018-01-01) - e.g. CqlIntervalOperatorsTest/Expand/ExpandPerDay.

Dispatch the boundary formatter on the cqf-cqlType extension's declared
point type (Date, DateTime or Time; System. prefix optional, DateTime
when absent). Date boundaries keep the @Date form and drop any anchored
time part.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qw9VWh3fvEPFMArBxnxtXF
@alexzautke alexzautke changed the title Extract time intervals from Periods via cqf-cqlType and render interval/quantity actuals in CQL syntax Extract Date/Time intervals from Periods via cqf-cqlType and render interval/quantity actuals in CQL syntax Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants